"""
Controller module for the epistasis monitor
"""

import wx
import gridepi_monitor_view
import threading
import time
import GUIconfig
import sys
import epiguilib
sys.path.append(GUIconfig.path_to_gridepistasis)
import gridepistasis
import cPickle
import webbrowser
import project.project as project
import os 
from gridepi_monitor_view import startup_progress_box

ANALYSIS_RUNNING_STATE = "Running"
ANALYSIS_FINISHED_STATE = "Finished"
ANALYSIS_STOPPED_STATE = "Stopped"


class BackgroundThread(threading.Thread):
    
    def __init__(self, func, *args):
        threading.Thread.__init__(self)
        self.func = func
        self.args = args
        self.output = None
            
    def run(self):
        self.func(self.args)

class StartEpistasisThread(threading.Thread):
    
    def __init__(self, parent, lock, args):
        threading.Thread.__init__(self)
        self.lock = lock
        self.args = args
        self._parent = parent
        
    def run(self):
        self.start_epistasis()
    
    
    def progress(self, msg, percentage):
        evt = BackgroundEvent(STARTUP_EVT_TYPE, STARTUP_PROGRESS_EVT_ID, progress=percentage, msg=msg)
        #me = threading.currentThread()
        wx.PostEvent(self._parent, evt)
        
        
    def start_epistasis(self):
        self.lock.acquire(True)
        #arg_list = list(self.args)
        gridepistasis.start_epistasis_from_UI(self.args, progress_callback=self.progress)
        self.progress("Startup complete.", 100)
        self.lock.release()


class UpdateEpistasisThread(threading.Thread):
    
    def __init__(self, wx_parent, status_file, local_mode, lock=None):
        threading.Thread.__init__(self)
        self.lock = lock
        self.status_file = status_file
        self.local_mode = local_mode
        
        self._parent = wx_parent
            
    def run(self):
        self.update_epistasis(self.status_file, self.local_mode)

    def update_epistasis(self, status_file, local_mode):
        if not self.lock.acquire(): # we cannot update right now so skip.
            print "Could not acquire lock. skipped update."
            return False
    
        pkl_file = open(status_file, "r")
        analysis_status = cPickle.load(pkl_file)
        pkl_file.close()
        
        jobs = gridepistasis.update_epistasis(analysis_status["jobs"], local_mode, analysis_status["result_dir"])
        analysis_status["jobs"] = jobs 
        
        if self.all_finished(jobs):
            analysis_status["state"] = ANALYSIS_FINISHED_STATE
            analysis_status["finished_time"] = time.asctime()
        
        else:
            analysis_status["state"] = ANALYSIS_RUNNING_STATE
        
        pkl_file = open(status_file, "w")
        cPickle.dump(analysis_status, pkl_file)
        pkl_file.close()
        
        self.lock.release()
        
        evt = BackgroundEvent(UPDATE_EVT_TYPE, UPDATE_EVT_ID)
        #me = threading.currentThread()
        wx.PostEvent(self._parent, evt)


    def all_finished(self, jobs):
        finished = True
        
        for j in jobs:
            finished = finished and j["status"] == "FINISHED" 
    
        return finished


class StopEpistasisThread(threading.Thread):
    
    def __init__(self, status_file, local_mode, lock=None):
        threading.Thread.__init__(self)
        
        self.status_file = status_file
        self.local_mode = local_mode
        self.lock = lock
        
    def run(self):
        self.StopEpistasis()
        
        
    def StopEpistasis(self):
        self.lock.acquire()
     
        pkl_file = open(self.status_file, "r")
        analysis_status = cPickle.load(pkl_file)
        pkl_file.close()
        
        gridepistasis.stop_epistasis(analysis_status["jobs"])
        
        self.lock.release()


UPDATE_EVT_TYPE = wx.NewEventType()
EVT_UPDATE = wx.PyEventBinder(UPDATE_EVT_TYPE, 1)
STARTUP_EVT_TYPE = wx.NewEventType()
EVT_STARTUP = wx.PyEventBinder(STARTUP_EVT_TYPE, 1)

UPDATE_EVT_ID = 1
STARTUP_PROGRESS_EVT_ID = 2


class BackgroundEvent(wx.PyCommandEvent):
    def __init__(self, etype, eid, msg=None, progress=None):
        """Init Result Event."""
        
        wx.PyCommandEvent.__init__(self, etype, eid)
        self.SetEventType(etype)
        self.msg = msg
        self.progress_percentage = progress
        self.eid = eid
        
    def GetMsg(self):
        return self.msg

    def GetProgress(self):
        return self.progress_percentage
    
    def task_complete(self):
        return (TASK_COMPLETE_EVT_ID == self.eid)

# Implementing Epistasis_monitor
class gridepi_monitor( gridepi_monitor_view.Epistasis_monitor):
    def __init__( self, epi_args, parent, resume=False):
        gridepi_monitor_view.Epistasis_monitor.__init__( self, parent )
        
        #startup_progress_box.__init__(self,parent)
        
        #elf.job_list.InsertColumn(0,"Data #1")
        self.jobs = [] 
        
        self.monitor_title.SetLabel("Epistasis monitor - %s" % project.get_current_project().name)
        
        #{"id":"1234", "status":"EXECUTING", "started":"1/12/13 12:14:51", "finished":"NA"},
        #            {"id":"2234", "status":"EXECUTING", "started":"1/12/13 12:14:52", "finished":"NA"}, 
        #            {"id":"4234", "status":"EXECUTING", "started":"1/12/13 12:14:53", "finished":"NA"}, 
        #            {"id":"6234", "status":"EXECUTING", "started":"1/12/13 12:14:54", "finished":"NA"}]*/
        self.local_mode = epi_args["local_mode"]
        self.gridepistasis_args = epi_args
        
        self.Bind( EVT_STARTUP, self.on_startup_progress)
        self.Bind( EVT_UPDATE, self.on_update_joblist)
        
        self.update_lock = threading.Lock()
        
        self.timer_thread = threading.Timer(5.0, self.update_timer) # needs to be started
        self.status_file = epi_args["status_file"]
        
        self.listed_jobs = []
        self.right_click_selected_job = None # this is necessary for remembering which job we right-clicked when selecting a sub menu. 
        
        self.setLabels() # set the column label names
        
        if not resume:
            self.start() # start epistasis in new thread
        else:
            self.update() # start the update interval
        
        
    def setLabels(self):
        self.job_list.InsertColumn(0,"",width=40)
        self.job_list.InsertColumn(1,"Job ID",width=200)
        self.job_list.InsertColumn(2,"Status",width=150)
        self.job_list.InsertColumn(3,"Started",width=150)
        self.job_list.InsertColumn(4,"Finished",width=150)
        
        
    def read_analysis_status(self):
        self.update_lock.acquire()
        
        pkl_file = open(self.status_file)
        analysis_status = cPickle.load(pkl_file)
        pkl_file.close()
        
        self.update_lock.release()
        
        
        return analysis_status
        
        
    def update_joblist(self):
        analysis_status = self.read_analysis_status()
        jobs = analysis_status["jobs"]
        
        self.job_list.DeleteAllItems()
        index = 0
        for j in jobs:
            self.insert_job(j, index)
            index += 1
            
        self.listed_jobs = jobs
        
        if analysis_status.has_key("state"):
            self.overall_status.SetLabel(analysis_status["state"])
        
        if analysis_status.has_key("jobs"):
            self.num_jobs.SetLabel(str(len(analysis_status["jobs"])))
        
        if analysis_status["state"] == ANALYSIS_RUNNING_STATE:
            self.timer_thread = threading.Timer(5.0, self.update_timer)
            self.timer_thread.start()
        
        if analysis_status.has_key("start_time"):
            self.started_time.SetLabel(analysis_status["start_time"])
        
        if analysis_status.has_key("finished_time"):
            self.finished_time.SetLabel(analysis_status["finished_time"])
    
    def insert_job(self, job, index):
        
        my_images = ["images/DNA_icon.jpg", "images/gene_network2_icon.jpg", "images/dna_small_icon.jpg"]

        self.job_list.il = wx.ImageList(40, 40)
    
        for i in my_images:
            self.job_list.il.Add(wx.Bitmap(i))
            
        self.job_list.SetImageList(self.job_list.il, wx.IMAGE_LIST_SMALL)
        
        genotype_icon = wx.Bitmap("images/DNA_icon.jpg")
        
        status = job["status"]
        
        #pos = self.job_list.InsertStringItem(index, str(job["id"]))
        self.job_list.InsertStringItem(index, "")
        self.job_list.SetItemImage(index, 2)
        self.job_list.SetStringItem(index, 1, str(job["id"]))
        self.job_list.SetStringItem(index,2,status)
        self.job_list.SetStringItem(index,3,job["started"])
        self.job_list.SetStringItem(index,4,job["finished"])
        
            
    def update_timer(self):    
        # update jobs
        print "update timer"
        self.update()
        #self.populate_joblist()t': 'epistasis.py', 'results_dir': 'EpistasisFiles25_1_2012_10914/', 'output_dir': '/home/benjamin/Dokumenter/epistasis-on-grid/trunk/grid_epistasis/epifiles', 'genotype_names': [u'rs16993553'], 'p_files': ['epicalcP.py'], 'resource_specs': {'VGRID'
           
        
    def update(self):
        print "updating"
        update_thread = UpdateEpistasisThread(self, self.status_file, self.local_mode, self.update_lock)
        update_thread.start()
        
    def start(self):    
        #self.gridepistasis_args.append(self.update_lock)
        #self.Enable(False)
        self.startup_box = startup_progress_box(self)
        #self.startup_box.Enable(True)
        #self.startup_box.SetPosition(wx.CENTER_FRAME)
        self.startup_box.Center()
        self.startup_box.cancel_startup_btn.Bind( wx.EVT_BUTTON, self.OnCancelStartup )
        
        #wx.DefaultPosition
        gridepi_thread = StartEpistasisThread(self, self.update_lock, self.gridepistasis_args)
        gridepi_thread.start()
    
    
    def open_summary_file(self, job):
        project_settings = project.get_current_project()
        result_dir = os.path.join(project_settings.epistasis_output_directory, job["analysis_name"])      
        summary_filename = GUIconfig.epistasis_job_summary_file_path.replace("CLASS", job["class"][0])
        summary_path = os.path.join(result_dir, summary_filename)
        if os.path.exists(summary_path):
            webbrowser.open(summary_path)
        else:
            dlg = wx.MessageDialog(self, "Could not find summary file at suspected path %s." % summary_path, "Summary file not found.", wx.ICON_ERROR)        
            dlg.ShowModal()
            
            webbrowser.open(result_dir)
    
        
    
    ### Event handlers ############
    
    
    
    def on_update_joblist(self, event):
        #self.update_status()
        self.update_joblist()
    
    def on_startup_progress(self, event):
        print "start event"
        progress = event.GetProgress()
        if progress == 100:
            #self.timer_thread.start()
            self.update()
            self.startup_box.Destroy()
            #self.Enable(True)
        else:
            self.startup_box.startup_progress_bar.SetValue(progress)
            self.startup_box.startup_progress_text.SetLabel(event.GetMsg())
       
        
    def on_stopped(self, event):
        
        self.update_lock.acquire()
        
        #self.timer_thread.cancel()
        
        pkl_file = open(self.status_file, "r")
        analysis_status = cPickle.load(pkl_file)
        pkl_file.close()
        
        #jobs = gridepistasis.update_epistasis(analysis_status["jobs"], local_mode, analysis_status["result_dir"])
        
        for j in analysis_status["jobs"]:
            j["status"] = "Cancelled" 
        
        #if self.all_finished(jobs):
        analysis_status["state"] = ANALYSIS_STOPPED_STATE
        analysis_status["finished_time"] = time.asctime()
        
        #else:
        #    analysis_status["state"] = ANALYSIS_RUNNING_STATE
        
        pkl_file = open(self.status_file, "w")
        cPickle.dump(analysis_status, pkl_file)
        pkl_file.close()
        
        
            
        self.update_lock.release()
        
        self.update_joblist()
        
        
    
    def OnStop( self, event):
        #self.update_lock.acquire()
        
        analysis_status = self.read_analysis_status()
        if analysis_status["state"] == ANALYSIS_RUNNING_STATE: 
        
            print "Stop"
            self.timer_thread.cancel()
        #self.update_joblist(set_status="Cancelled")
        #cancel_thread = StopEpistasisThread(self.status_file, self.local_mode, self.update_lock)
        #cancel_thread.start()
        #self.update_lock.release()
        #event.Skip()
            analysis_status = self.read_analysis_status()
        #self.update_lock.acquire()
     
        #pkl_file = open(self.status_file, "r")
        # cPickle.load(pkl_file)
        #pkl_file.close()
        
        #gridepistasis.stop_epistasis(analysis_status["jobs"])
            print "before cancel thread"

            epiguilib.run_in_thread(gridepistasis.stop_epistasis, (analysis_status["jobs"],), self.on_stopped, self)
        else:
            print "Not running. Stop is ignored."
        #self.lock.release()

        
    
    def OnClose( self, event ):
        
        #self.timer_thread.cancel()
        self.GetTopLevelParent().remove_page_tab(self)
        #self.timer_thread.cancel()

        #self.Close()
        #threads = threading.enumerate()
        #for t in threads:
        #    if t != threading.current_thread():
        #        t.exit()
        
    def OnDClick(self, event):
        #print "double click"
        
        #obj = event.GetEventObject()
        #print obj
        #print event.GetEventType() 
        selected_job = self.listed_jobs[event.m_itemIndex]
        #print "index", event.m_itemIndex
        #print selected_job 
        #GetFocusedItem(self) 
        #project_settings = project.get_current_project()
        #result_dir = os.path.join(project_settings.epistasis_output_directory, selected_job["analysis_name"])      
 #self.right_click_selected_job.
 
        if selected_job["status"] in ("FINISHED"):
            self.open_summary_file(selected_job)
        else:
            dlg = wx.MessageDialog(self, "Please wait until the job has finished.", "Job not finished error", wx.ICON_ERROR)        
            dlg.ShowModal()
            
            
        
    def OnRightClick( self, event ):
        # TODO: Implement OnRightClick
        #self.rightclick_menu.Show()
        #position = event.GetPosition()
        #print event.GetSelectedCells()
        self.right_click_selected_job = self.listed_jobs[event.m_itemIndex] 
        
        if self.right_click_selected_job["status"] in ("EXECUTING", "QUEUED"):
            self.menu_cancel_job.Enable(True)
            self.menu_files.Enable(False)
            
        if self.right_click_selected_job["status"] in ("FINISHED"):
            self.menu_cancel_job.Enable(False)
            self.menu_files.Enable(True)
            self.menu_open_summary.Enable(True)
    
        self.PopupMenu(self.rightclick_menu, wx.DefaultPosition)
            
    def OnOpenSummary(self, event):
        self.open_summary_file(self.right_click_selected_job)
    
    def OnGotoFiles( self, event ):
        #print event.GetEventObject()
        #print event.m_itemIndex
        project_settings = project.get_current_project()
        result_dir = os.path.join(project_settings.epistasis_output_directory, self.right_click_selected_job["analysis_name"])      
 #self.right_click_selected_job.
        webbrowser.open(result_dir)
        
        #event.Skip()
    
    def OnCancelJob( self, event ):
        job_list = event.GetEventObject()
        
        #item = job_list.GetNextItem()
        #item.
        #event.Skip()                                        
                  
                  
    def OnCancelStartup(self, event):
        self.Destroy()          
        
if __name__=="__main__":
    #os.chdir(GUIconfig.root_directory)
    
    result_dir = '/home/benjamin/Dokumenter/epiprojects/my_project20112/application_files/epistasis/output_files/my_analysis' 
    status_file = "/home/benjamin/my_project15/application_files/epistasis/status_files/analysis_Fri_Jun__1_103849_2012_status.pkl"
    
    download_dir = "/home/benjamin/Dokumenter/epistasis-on-grid/trunk/grid_epistasis/epifiles/my_analysis"
    
    #if os.path.exists(download_dir):
    #    shutil.rmtree(download_dir)
    
    #if os.path.exists(result_dir):
    #    shutil.rmtree(result_dir)
    
    #if os.path.exists(status_file):
    #    os.remove(status_file)
    
    test_args = (u'sex', (u'1',u'2'), [(u'rs16993553', "SOMEGENE4")], '/home/benjamin/Dokumenter/epistasis-on-grid/trunk/GUI/genotype_matrix.dat', [u'SysSitting'], '/home/benjamin/Dokumenter/epistasis-on-grid/trunk/GUI/phenotype_matrix.dat', u'my_analysis', True, status_file, result_dir)
    #status_file = "/home/benjamin/Dokumenter/epiprojects/my_project20112/application_files/epistasis/status_files/my_analysis_status.pkl"
    local = True
    app = wx.App(False)
    
    frame = wx.Frame(None, wx.ID_ANY, "Hello World", size=(1000,800)) # A Frame is a top-level window.
    
    gridepi_monitor(status_file, local, test_args, parent=frame, resume=True)
    
    frame.Show()
    #frame.launch_wizard()
    #frame.load_recent_projects()
    app.MainLoop()    
    